home *** CD-ROM | disk | FTP | other *** search
- /*** Complete listing (1) -- CLOCKINT.C, MYFUNC.C, SERIAL.C, COLORS.H ***/
-
-
- /* CLOCKINT.C : Turbo C program to install your own interrupt
- function in the clock interrupt, while still preserving everyone
- else's. Bruce Eckel, Eisys Consulting 1987. To make the control
- program, you must create a project file which contains the
- following, and make it's file the "project name": colors.h is a
- header which the C file includes; if the header gets changed,
- that C file will be re-compiled even if it wasn't modified
- itself. */
-
- #define CLOCKINT 8 /* IRQ0 on the 8259 is interrupt 8 in the
- vector table */
-
- #define PROG_SIZE 0x87F /* Run the Turbo C compiler with the
- options:linker:mapfile set to "segments." Look at the mapfile
- generated for this program. The "stop" address for the stack is
- the highest adress used -- set PROG_SIZE to this value for use
- with the "keep()" command */
-
- void interrupt (*old_int_handler)();
-
- /* This, believe it or not, is a variable declaration. I've put
- it way out here (outside of any function declarations) so it
- doesn't go away when main exits (i.e. it's global). It holds the
- address of the old interrupt handler function so the NEW
- interrupt handler can call the OLD one when it's through. To read
- it, start in the middle and work your way out: old_int_handler is
- a POINTER ("*") to a FUNCTION ("()") of type "interrupt"; this
- function doesn't return a value ("void"). */
-
- main()
- /* all this does is install the interrupt handler. I
- don't use any "printf()s" because it increases the
- resident program size so much. */
- {
-
- /* first: function prototypes, right out of the book. These
- don't generate any code, but they tell the compiler (and us)
- exactly how the functions should be called so it can catch
- mistakes. The same effect can be achieved with "#include
- <dos.h>", but this is more educational. */
-
- extern void interrupt (*getvect(int intr_num))();
- /* The above declaration is tricky to read: getvect returns a
- pointer to a function of type interrupt (and an "interrupt"
- function never returns a value, thus the "void"). It's deceiving
- because the function "getvect" is actually returning something,
- but the first word you see is "void" */
-
- extern void setvect(int intr_num, void interrupt (*isr)());
- /* This function really DOESN'T return return anything. It must
- be passed an integer and a pointer to a function of type
- interrupt which has no return value. The ability to pass
- function addresses around allows us to almost entirely eliminate
- the need for assembly language when using C. */
-
- void interrupt my_int_handler();
- /* Prototype for the interrupt we are inserting in the clock
- chain (a lot like inserting an item in a linked list). Notice
- the word "extern" doesn't appear in this declaration because the
- function definition is contained in this file. */
-
- /* The code is trivial: get the old clock interrupt vector (for
- our routine to pass control to when it's done), install our
- routine as the new interrupt service routine, and call "terminate
- but stay ready." */
-
- old_int_handler = getvect(CLOCKINT); /* store the address of the old
- handler */
- setvect(CLOCKINT, my_int_handler);
- keep(0,PROG_SIZE); /* first parameter is exit status */
- }
-
- void interrupt my_int_handler()
- {
- extern void my_function(); /* i.e.: this is defined in another file */
- const int clock_count = 9; /* wait this many ints between invoking our
- function; there are about 18 interrupts per second. */
- static int i = 0; /* The interrupt counter doesn't go away
- between interrupts */
-
- if(i++ == clock_count) { my_function(); i = 0;} /* Do whatever we want... */
- /* (with the usual caveat that we can't make any DOS or BIOS calls) */
- (*old_int_handler)(); /* ... and continue the chain by de-referencing the
- pointer to the old clock service routine. */
- /* This was just TOO easy !! */
- }
-
- /* A caveat: I haven't done extensive testing, so this could slow
- down the time-of-day clock on the PC. If it does, one might add
- a function to restore the correct time from the battery-backed
- clock-calendar. */
-
-
-
-
-
- /* MYFUNC.C: Turbo C function installed as a TSR by CLOCKINT.C.
- Bruce Eckel, Eisys Consulting 1987. This integrates the serial
- input and output functions, and some screen display code from
- issue #38 (which must be used since an interrupt routine can't
- make DOS calls; i.e. DOS isn't re-entrant). Each time
- my_function() is called, it outputs a new character from the
- led_table[] to the serial-to-parallel chip, gets a byte from the
- parallel-to-serial chip, and displays the byte as ones and zeroes
- in the lower right corner of the screen.
-
- You can replace the code in my_function() with anything else
- (as long as your code or any C library functions you call don't
- make DOS or ROM BIOS calls -- printf() does) to create your own
- clock interrupt background process. */
-
- #include "colors.h" /* #defines for the CGA */
-
- /* Now we tell the compiler that "led_table[]" is in another
- file. We still have to show what it looks like so the compiler
- can generate the right code when we reference elements of the
- structure. The actual address of led_table[] is figured out
- ("resolved") by the linker. This type of declaration is often
- contained in a header file as a "typedef." The header file is
- then included in the file where the table is defined and any
- other files where it is declared. Note, however, that a header
- file with anything other than definitions in it (i.e. code) is
- asking for trouble. */
-
- extern const struct {
- char character;
- unsigned char code;
- } led_table[];
-
- /* Here's the function which is called by my_int_handler() */
- void my_function()
- {
- void display_byte(unsigned char byte);
- extern void outbyte(unsigned char ch);
- extern unsigned char inbyte();
-
- static int i = 0; /* statics don't go away between function
- calls, so we don't output the first character every time. It's
- only initialized to zero when the program is loaded. */
-
- display_byte(inbyte()); /* Read the LS165 and display it on the screen. */
- outbyte(led_table[i].code); /* send a character to the seven-segment
- display.*/
- if(led_table[++i].code == 0) /* increment the table index and see if
- we're at the end of the table; */
- i = 0; /* if so, start over. */
- }
-
- /* This function displays a byte as ones and zeroes in the lower
- right corner of the CGA screen, without calling printf(). */
-
- void display_byte(unsigned char byte)
- {
- void putc_at_location(char, int, int, unsigned char);
-
- /* you don't HAVE to give variable names in a declaration, but it's not
- as clear if you don't. */
-
- int i, column = 64; /* "line" and "column" are the starting locations */
- const int line = 24; /* The compiler barfs if we try to change a constant.*/
- for( i = 7; i >= 0; i--){ /* start with the high bit and work down */
- putc_at_location(byte & (1 << i) ? '1' : '0', column++, line,
- LIGHT_GREEN_CHAR | RED_BACK);
- /* See SERIAL.C for a description of the "?:" ternary expression. */
- putc_at_location(' ', column++, line, BLACK_CHAR | RED_BACK);
- }
- }
-
- /* Puts a character and its attribute anywhere on the screen. */
- void putc_at_location(char ch, int x, int y, unsigned char attribute)
- {
- pokeb(SCREEN_BASE,((y * SCREEN_WIDTH) + x) * 2,ch);
- pokeb (SCREEN_BASE,(((y * SCREEN_WIDTH) + x) * 2) + 1, attribute);
- }
-
-
-
-
-
- /* SERIAL.C: Turbo C program to write to a TTL LS164 and read
- from an LS165 through a parallel printer card. Bruce Eckel,
- Eisys Consulting 1987. You can create either a stand-alone
- program for testing, or link the compiled file into the
- "CLOCKINT.C" project using CLOCKINT.PRJ. */
-
- #undef test /* #define test creates a stand-alone test program
- from this file */
- #define BASE 0x238 /* I modified my $21 Microsphere card so it's in an
- unused address space (no collisions with other
- printer cards) */
-
- #define BIT(I) (unsigned char)(1 << I) /* this should be evaluated
- at compile time so there won't be any overhead at
- run time. The "(unsigned char)" is called a "cast"
- (like casting into a mold); it forces the result to
- be that data type (in this case, one byte long and
- don't mess with the high bit). */
-
- /* These definitions make the code much easier to follow (and
- thus less prone to error). "ORing" with a bit sets that bit to
- "1", while "ANDing" with the complement of a bit sets that bit to
- zero. I'm being sort of tricky here: BASE is the printer data
- address and can be read from as well as written to, so I read it,
- change a single bit, and write it out again so the other bits
- aren't changed and can be used for other applications. You can't
- do this with the other printer port locations (BASE+1 and
- BASE+2). It would probably be more proper to use global memory
- locations to preserve the state of all three. */
-
- /* The output clock for the LS164: */
- #define LOWER_CLOCK_O outport(BASE,inport(BASE) & ~BIT(0)) /* pin 2
- on DB-25 */
- #define RAISE_CLOCK_O outport(BASE,inport(BASE) | BIT(0))
- /* Changing the output data bit for the LS164: */
- #define DATA_0 (inport(BASE) & ~BIT(1)) /* pin 3 on
- DB-25 */
- #define DATA_1 (inport(BASE) | BIT(1))
- /* Load and shift control for the LS165: */
- #define LOAD_LS165 outport(BASE, inport(BASE) & ~BIT(2)) /* pin 4 on
- DB-25 */
- #define SHIFT_LS165 outport(BASE, inport(BASE) | BIT(2))
- /* The input clock for the LS165: */
- #define LOWER_CLOCK_I outport(BASE,inport(BASE) & ~BIT(3)) /* pin 5 on
- DB-25 */
- #define RAISE_CLOCK_I outport(BASE,inport(BASE) | BIT(3))
-
- /* This is a table with characters and the codes which generate them on
- the seven-segment display: */
- const struct { /* Notice the array declaration led_table[]. */
- char character; /* You don't have to put the number of array */
- unsigned char code; /* entries in the brackets; the compiler */
- } led_table[] = { /* counts them for you! */
- { '0', 0x3f}, /* The seven-segment display: */
- { '1', 0x30}, /* */
- { '2', 0x5b}, /* a: bit 0 */
- { '3', 0x4f}, /* ________ */
- { '4', 0x66}, /* | | */
- { '5', 0x6d}, /* f: bit 5 | | b: bit 1 */
- { '6', 0x7d}, /* | g: | */
- { '7', 0x07}, /* |________| */
- { '8', 0x7f}, /* | bit 6 | */
- { '9', 0x6f}, /* e: bit 4 | | c: bit 2 */
- { 'A', 0x77}, /* | | */
- { 'B', 0x7c}, /* |________| */
- { 'C', 0x39}, /* d: bit 3 */
- { 'D', 0x5e}, /* [*] */
- { 'E', 0x79}, /* h: bit 7 */
- { 'F', 0x71}, /* */
- { 'H', 0x76}, /* For example, the number 5 would use segments: */
- { 'J', 0x1e}, /* a, c, d, f & g which forms the binary number: */
- { 'L', 0x38}, /* 01101101 or the hex number 0x6d */
- { 'N', 0x54}, /* */
- { 'O', 0x3f}, /* Some of the letters require a stretch of the */
- { 'P', 0x73}, /* imagination, but they get the message across. */
- { 'R', 0x50}, /* */
- { 'S', 0x6d},
- { 'T', 0x78},
- { 'U', 0x3E},
- { 'Y', 0x6E},
- { '?', 0x53},
- { '.', 0x80},
- { '-', 0x40},
- { '=', 0x48},
- { '_', 0x08},
- { '\0',0} /* this tags the end of the table, so new entries can be
- added without any other code modification. */
- };
-
- #if defined(test) /* for stand-alone testing */
- main()
- { /* function declarations first: */
- void outbyte(unsigned char ch);
- unsigned char inbyte();
- void print_byte(unsigned char byte);
- int i = 0;
- char ch;
- do {
- print_byte(inbyte()); /* get a byte from the parallel-to-serial chip
- and display it in binary */
- ch = toupper(getch()); /* get a keyboard character and convert to
- upper case */
- /* search the table until you find the character or hit the end. */
- for(i = 0; led_table[i].character != ch && led_table[i].code != 0; i++)
- ; /* All the work is done inside the "for" parentheses. */
- outbyte(led_table[i].code); /* send the code to the
- serial-to-parallel chip */
- } while ( ch != 27 ); /* repeat until the "escape" key is pressed */
- } /* end of main */
-
- /* I'm putting this definition inside the "#if (defined)" conditional
- compilation because the code for printf() is very big and I don't want
- it included when the Terminate-And-Stay-Ready program is created. */
-
- void print_byte(unsigned char byte)
- {
- int i;
- for(i = 7; i >=0; i--)
- byte & (1 << i) ? printf("1 ") : printf("0 ");
- /* See below for description of the "?:" ternary expression. */
- printf("\n");
- }
-
- #endif defined(test) /* You can put things after an "#endif" as notes to
- yourself; the compiler ignores them. */
-
- /* And now, the function definitions: */
- void outbyte(unsigned char byte)
- /* Send a byte to the serial-to-parallel chip (LS164) */
- {
- int i;
- RAISE_CLOCK_O; /* This is just for the benefit of the initial call. */
- for(i = 7; i >= 0; i--){ /* I start with 7 because that bit goes out first */
- LOWER_CLOCK_O;
- /* Now present the data at pin 1 of the DB-25 */
- outport(BASE, byte & (1 << i) ? DATA_0 : DATA_1); /* Outputs are
- inverted */
- /* The "ternary expression" returns the value to the left of the ":"
- of the question is true (i.e. non-zero) and the value to the right
- of the ":" if the question is false (i.e. zero). It's very nifty
- and compact, since it not only performs an "if-then-else," but it
- also returns a value. */
-
- RAISE_CLOCK_O; /* clock the data in */
- }
- }
-
- unsigned char inbyte()
- /* read a value from the parallel-to-serial chip (LS165) */
- {
- int i;
- unsigned char result = 0;
- RAISE_CLOCK_I; /* Clock should be high before loading data into the chip */
- LOAD_LS165; /* Move the data at the inputs into the flip-flops */
- SHIFT_LS165; /* Enable shifting out of the data */
- for(i = 7; i >= 0; i--) { /* bit 7 comes out of the LS165 first */
- /* Bit 7 is present at the output immediately when the chip is loaded,
- so we have to pluck it off (into pin 12 of the DB-25) before we do
- any clocking. */
- result |= ((inport(BASE+1) & BIT(5)) ? 1 : 0) << i;
- /* form result: The ternary expression returns 1 if bit 5 of i/o
- address BASE+1 is a "1" and 0 if it's a zero. This value is
- shifted into the appropriate bit position by the "<< i".
- result |= is the same as saying: result = result |, so the bit is
- ORed into the result. This way, we poke each serial bit into the
- correct place in the parallel result. */
- LOWER_CLOCK_I;
- RAISE_CLOCK_I; /* The next bit appears on the rising edge of
- the clock. */
- }
- return result;
- }
-
-
-
-
- /* COLORS.H: definitions for CGA screen characteristics and colors */
- #define SCREEN_BASE 0xb800 /* base address of color graphics card (and EGA
- in color graphics mode */
- #define SCREEN_HEIGHT 25
- #define SCREEN_WIDTH 80
- #define SCREEN_CHARS (SCREEN_WIDTH * SCREEN_HEIGHT * 2)
- /* number of chars and attributes in a screen */
-
- #define BIT(I) (unsigned char)(1 << I) /* A bit mask macro. The compiler
- evaluates it, so it doesn't cost anything. */
-
- /* Make a complete attribute by ORing a CHARacter type with a
- BACKground type */
- #define BLUE_CHAR BIT(0)
- #define GREEN_CHAR BIT(1)
- #define RED_CHAR BIT(2)
- #define INTENSE BIT(3)
- #define BLUE_BACK BIT(4)
- #define GREEN_BACK BIT(5)
- #define RED_BACK BIT(6)
- #define BLINKING BIT(7)
-
- #define BLACK_CHAR 0
- #define CYAN_CHAR (GREEN_CHAR | BLUE_CHAR)
- #define MAGENTA_CHAR (RED_CHAR | BLUE_CHAR)
- #define BROWN_CHAR (RED_CHAR | GREEN_CHAR)
- #define WHITE_CHAR (RED_CHAR | GREEN_CHAR | BLUE_CHAR)
- #define GRAY_CHAR (INTENSE | BLACK_CHAR)
- #define LIGHT_BLUE_CHAR (INTENSE | BLUE_CHAR)
- #define LIGHT_GREEN_CHAR (INTENSE | GREEN_CHAR)
- #define LIGHT_CYAN_CHAR (INTENSE | CYAN_CHAR)
- #define LIGHT_RED_CHAR (INTENSE | RED_CHAR)
- #define LIGHT_MAGENTA_CHAR (INTENSE | MAGENTA_CHAR)
- #define YELLOW_CHAR (INTENSE | BROWN_CHAR)
- #define BRIGHT_WHITE_CHAR ( INTENSE | WHITE_CHAR)
-
- #define BLACK_BACK 0
- #define CYAN_BACK (GREEN_BACK | BLUE_BACK)
- #define MAGENTA_BACK (RED_BACK | BLUE_BACK)
- #define BROWN_BACK (RED_BACK | GREEN_BACK)
- #define WHITE_BACK (RED_BACK | GREEN_BACK | BLUE_BACK)
- #define GRAY_BACK (INTENSE | BLACK_BACK)
- #define LIGHT_BLUE_BACK (INTENSE | BLUE_BACK)
- #define LIGHT_GREEN_BACK (INTENSE | GREEN_BACK)
- #define LIGHT_CYAN_BACK (INTENSE | CYAN_BACK)
- #define LIGHT_RED_BACK (INTENSE | RED_CHAR)
- #define LIGHT_MAGENTA_BACK (INTENSE | MAGENTA_CHAR)
- #define YELLOW_BACK (INTENSE | BROWN_BACK)
- #define BRIGHT_WHITE_BACK ( INTENSE | WHITE_BACK)
-